﻿using System;
using System.IO;

namespace Pfz.Remoting
{
	/// <summary>
	/// This class represents a communication stream that uses MemoryMappedFiles as its sharing resource.
	/// It is bidirectional
	/// </summary>
	public class MemoryMappedFileStream:
		Stream
	{
		private MemoryMappedFileWriter _writer;
		private MemoryMappedFileReader _reader;

		private MemoryMappedFileStream()
		{
		}

		/// <summary>
		/// Creates a new memory-mapped-file communication, as the server.
		/// </summary>
		public static MemoryMappedFileStream CreateAsServer(string name, int length, int timeout=-1)
		{
			MemoryMappedFileStream result = new MemoryMappedFileStream();
			result.Name="Mmf:" + name;
			result._writer = new MemoryMappedFileWriter(name + ":2", length, timeout);
			try
			{
				result._reader = new MemoryMappedFileReader(name + ":1", timeout);
			}
			catch
			{
				result.Dispose();
				throw;
			}
			return result;
		}

		/// <summary>
		/// Creates a new memory-mapped-file communication, as the client.
		/// </summary>
		public static MemoryMappedFileStream CreateAsClient(string name, int timeout=-1)
		{
			MemoryMappedFileStream result = new MemoryMappedFileStream();
			result.Name = "Mmf:" + name;
			var reader = new MemoryMappedFileReader(name + ":2", timeout);
			try
			{
				result._reader = reader;
				result._writer = new MemoryMappedFileWriter(name + ":1", reader.MemoryMappedFileLength, timeout);
			}
			catch
			{
				result.Dispose();
				throw;
			}

			return result;
		}

		/// <summary>
		/// Releases the reader and writer.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				Disposer.Dispose(ref _writer);
				Disposer.Dispose(ref _reader);
			}

			base.Dispose(disposing);
		}

		/// <summary>
		/// Always returns true.
		/// </summary>
		public override bool CanTimeout
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// Gets the Name of this MemoryMappedFileStream.
		/// </summary>
		public string Name
		{
			get; private set;
		}

		/// <summary>
		/// Gets or sets the read-timeout.
		/// </summary>
		public override int ReadTimeout
		{
			get
			{
				return _reader.Timeout;
			}
			set
			{
				_reader.Timeout = value;
			}
		}

		/// <summary>
		/// Gets or sets the write-timeout.
		/// </summary>
		public override int WriteTimeout
		{
			get
			{
				return _writer.Timeout;
			}
			set
			{
				_writer.Timeout = value;
			}
		}

		/// <summary>
		/// Always returns true.
		/// </summary>
		public override bool CanRead
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// Always returns false.
		/// </summary>
		public override bool CanSeek
		{
			get
			{
				return false;
			}
		}

		/// <summary>
		/// Always returns true.
		/// </summary>
		public override bool CanWrite
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// Tells the other side that it can start reading.
		/// </summary>
		public override void Flush()
		{
			_writer.Flush();
		}

		/// <summary>
		/// Not supported.
		/// </summary>
		public override long Length
		{
			get { throw new NotSupportedException(); }
		}

		/// <summary>
		/// Not supported.
		/// </summary>
		public override long Position
		{
			get
			{
				throw new NotSupportedException();
			}
			set
			{
				throw new NotSupportedException();
			}
		}

		/// <summary>
		/// Reads data to the given buffer.
		/// </summary>
		public override int Read(byte[] buffer, int offset, int count)
		{
			return _reader.Read(buffer, offset, count);
		}

		/// <summary>
		/// Not supported.
		/// </summary>
		public override long Seek(long offset, SeekOrigin origin)
		{
			throw new NotSupportedException();
		}

		/// <summary>
		/// Not supported.
		/// </summary>
		public override void SetLength(long value)
		{
			throw new NotSupportedException();
		}

		/// <summary>
		/// Writes the buffer data to the stream.
		/// </summary>
		public override void Write(byte[] buffer, int offset, int count)
		{
			_writer.Write(buffer, offset, count);
		}

		/// <summary>
		/// Reads a single byte of data. Returns -1 if connection is lost.
		/// </summary>
		public override int ReadByte()
		{
			return _reader.ReadByte();
		}

		/// <summary>
		/// Writes a single byte of data.
		/// </summary>
		public override void WriteByte(byte value)
		{
			_writer.WriteByte(value);
		}
	}
}
